﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Web.Security;
using System.DirectoryServices;
using System.Configuration;
using MP.Core.Library;
using System.Runtime.InteropServices;
namespace MP.Core.Authentication
{
    public class LoginAuthentication
    {
        public const string LoginNothing = "Nothing";
        public const string LoginFail = "LoginFail";
        public const string LoginUserEmpty = "UserEmpty";
        public const string LoginSuccess = "LoginSuccess";

        public const string CodeEncrypt = "!@#mpp0rt@l2010";
        public static LoginAuthentication CreateInstant()
        {
            return new LoginAuthentication();
        }

        #region Method general
        public static string DecodePass(string password)
        {
            try
            {
                System.Text.UTF8Encoding encoder = new System.Text.UTF8Encoding();
                System.Text.Decoder utf8Decode = encoder.GetDecoder();

                byte[] todecode_byte = Convert.FromBase64String(password);
                int charCount = utf8Decode.GetCharCount(todecode_byte, 0, todecode_byte.Length);
                char[] decoded_char = new char[charCount];
                utf8Decode.GetChars(todecode_byte, 0, todecode_byte.Length, decoded_char, 0);
                string result = new String(decoded_char);
                return result;
            }
            catch (Exception e)
            {
                return "";
            }
        }

        public static string GetDomainName()
        {
            string loginDomainName = "mptelecom";
            if (!string.IsNullOrEmpty(ConfigurationManager.AppSettings["loginDomainName"]))
                loginDomainName = ConfigurationManager.AppSettings["loginDomainName"];
            return loginDomainName;
        }
        #endregion

        #region Auto Login Domain by SID
        private string GetSid(string strLogin)
        {
            string str = "";
            // Parse the string to check if domain name is present.
            int idx = strLogin.IndexOf('\\');
            if (idx == -1)
            {
                idx = strLogin.IndexOf('@');
            }

            string strDomain;
            string strName;

            if (idx != -1)
            {
                strDomain = strLogin.Substring(0, idx);
                strName = strLogin.Substring(idx + 1);
            }
            else
            {
                strDomain = Environment.MachineName;
                strName = strLogin;
            }


            DirectoryEntry obDirEntry = null;
            try
            {
                Int64 iBigVal = 5;
                Byte[] bigArr = BitConverter.GetBytes(iBigVal);
                obDirEntry = new DirectoryEntry("WinNT://" + strDomain + "/" + strName);
                System.DirectoryServices.PropertyCollection coll = obDirEntry.Properties;
                object obVal = coll["objectSid"].Value;
                if (null != obVal)
                {
                    str = ConvertByteToStringSid((Byte[])obVal);
                }
                Console.WriteLine("====");
                foreach (object obj in coll.PropertyNames)
                {
                    Console.WriteLine(obj.ToString());
                }
                Console.WriteLine("====");
            }
            catch (Exception ex)
            {
                str = "";
            }
            return str;
        }

        private string ConvertByteToStringSid(Byte[] sidBytes)
        {
            short sSubAuthorityCount = 0;
            StringBuilder strSid = new StringBuilder();
            strSid.Append("S-");
            try
            {
                // Add SID revision.
                strSid.Append(sidBytes[0].ToString());

                sSubAuthorityCount = Convert.ToInt16(sidBytes[1]);

                // Next six bytes are SID authority value.
                if (sidBytes[2] != 0 || sidBytes[3] != 0)
                {
                    string strAuth = String.Format("0x{0:2x}{1:2x}{2:2x}{3:2x}{4:2x}{5:2x}",
                                (Int16)sidBytes[2],
                                (Int16)sidBytes[3],
                                (Int16)sidBytes[4],
                                (Int16)sidBytes[5],
                                (Int16)sidBytes[6],
                                (Int16)sidBytes[7]);
                    strSid.Append("-");
                    strSid.Append(strAuth);
                }
                else
                {
                    Int64 iVal = (Int32)(sidBytes[7]) +
                            (Int32)(sidBytes[6] << 8) +
                            (Int32)(sidBytes[5] << 16) +
                            (Int32)(sidBytes[4] << 24);
                    strSid.Append("-");
                    strSid.Append(iVal.ToString());
                }

                // Get sub authority count...
                int idxAuth = 0;
                for (int i = 0; i < sSubAuthorityCount; i++)
                {
                    idxAuth = 8 + i * 4;
                    UInt32 iSubAuth = BitConverter.ToUInt32(sidBytes, idxAuth);
                    strSid.Append("-");
                    strSid.Append(iSubAuth.ToString());
                }
            }
            catch (Exception ex)
            {
                return "";
            }
            return strSid.ToString();
        }

        public bool CheckLoginDomain()
        {
            string sid = HttpContext.Current.Request.LogonUserIdentity.User.Value;
            string loginName = HttpContext.Current.Request.LogonUserIdentity.Name;
            string sidDomain = GetSid(loginName);
            return sid == sidDomain;
        }
        #endregion

        #region Login Domain
        [DllImport("ADVAPI32.dll", EntryPoint = "LogonUserW", SetLastError = true, CharSet = CharSet.Auto)]
        public static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

        string GetDomainName(string usernameDomain)
        {
            if (usernameDomain.Contains("\\"))
            {
                int index = usernameDomain.IndexOf("\\");
                return usernameDomain.Substring(0, index);
            }
            else if (usernameDomain.Contains("@"))
            {
                int index = usernameDomain.IndexOf("@");
                return usernameDomain.Substring(index + 1);
            }
            else
            {
                return "";
            }
        }

        string GetUsername(string usernameDomain)
        {
            if (usernameDomain.Contains("\\"))
            {
                int index = usernameDomain.IndexOf("\\");
                return usernameDomain.Substring(index + 1);
            }
            else if (usernameDomain.Contains("@"))
            {
                int index = usernameDomain.IndexOf("@");
                return usernameDomain.Substring(0, index);
            }
            else
            {
                return usernameDomain;
            }
        }

        public string CheckLoginDomain(string user, string pass)
        {
            string status = LoginNothing;
            string domainName = GetDomainName(user); // Extract domain name form provide DomainUsername e.g Domainname\Username
            string userName = GetUsername(user);  // Extract user name from provided DomainUsername e.g Domainname\Username
            IntPtr token = IntPtr.Zero;

            //userName, domainName and Password parameters are very obvious.
            //dwLogonType (3rd paramter): I used LOGON32_LOGON_INTERACTIVE, This logon type is intended for users who will be interactively using the computer, such as a user being logged on by a terminal server, remote shell, or similar process. This logon type has the additional expense of caching logon information for disconnected operations. For more details about this parameter please see http://msdn.microsoft.com/en-us/library/aa378184(VS.85).aspx
            //dwLogonProvider (4th parameter) : I used LOGON32_PROVIDER_DEFAUL, This provider use the standard logon provider for the system. The default security provider is negotiate, unless you pass NULL for the domain name and the user name is not in UPN format. In this case, the default provider is NTLM. For more details about this parameter please see http://msdn.microsoft.com/en-us/library/aa378184(VS.85).aspx
            //phToken (5th parameter): A pointer to a handle variable that receives a handle to a token that represents the specified user. We can use this handler for impersonation purpose. 
            if (string.IsNullOrEmpty(domainName)) domainName = GetDomainName();
            bool result = LogonUser(userName, domainName, pass, 2, 0, ref token);
            if (result)
            {
                MembershipUser u = Membership.GetUser(userName);
                if (u == null)
                {
                    status = LoginUserEmpty;
                }
                else
                {
                    status = LoginSuccess;
                }
            }
            else
            {
                status = LoginFail;
            }
            return status;
        }
        #endregion
    }
}
